﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

using Microsoft.VisualStudio;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.WowAddonStudio.Lua.LanguageService.Model;
using Microsoft.WowAddonStudio.Lua.Model;
using Microsoft.WowAddonStudio.Lua.Support;

namespace Microsoft.WowAddonStudio.Lua.LanguageService.Intellisense.Providers
{
    public class SnippetDeclarationProvider : SimpleDeclarationProvider
    {
        private readonly LuaLanguageService languageService;

        /// <summary>
        /// Initializes a new instance of the <see cref="SnippetDeclarationProvider"/> class.
        /// </summary>
        /// <param name="languageService">The language service.</param>
        public SnippetDeclarationProvider(LuaLanguageService languageService)
        {
            if (languageService == null)
                throw new ArgumentNullException("languageService");

            this.languageService = languageService;
        }

        /// <summary>
        /// Gets the complete word declarations.
        /// </summary>
        /// <returns></returns>
        protected override IEnumerable<Declaration> GetSimpleDeclarations()
        {
            return this.GetExpansions().Select(expansion => DeclarationFactory.CreateDeclaration<Snippet>(expansion.shortcut, expansion.description)).Cast<Declaration>();
        }

        /// <summary>
        /// Gets the snippets.
        /// </summary>
        /// <returns></returns>
        private IEnumerable<VsExpansion> GetExpansions()
        {
            var textManager = (IVsTextManager)languageService.GetService(typeof(SVsTextManager));
            if (textManager != null)
            {
                var textManager2 = (IVsTextManager2)textManager;

                IVsExpansionManager expansionManager;
                if (ErrorHandler.Succeeded(textManager2.GetExpansionManager(out expansionManager)))
                {
                    // Tell the environment to fetch all of our snippets.
                    IVsExpansionEnumeration expansionEnumerator;
                    if (ErrorHandler.Succeeded(expansionManager.EnumerateExpansions(Guids.LuaLanguageService,
                                                                                    0,     // return all info
                                                                                    null,    // return all types
                                                                                    0,     // return all types
                                                                                    1,     // include snippets without types
                                                                                    0,     // do not include duplicates
                                                                                    out expansionEnumerator)))
                    {
                        // Cache our expansions in an array of VSExpansion structures.
                        uint count;
                        var expansionInfo = new VsExpansion();
                        var pExpansionInfo = new IntPtr[1];

                        // Allocate enough memory for one VSExpansion structure.
                        // This memory is filled in by the Next method.
                        pExpansionInfo[0] = Marshal.AllocCoTaskMem(Marshal.SizeOf(expansionInfo));

                        expansionEnumerator.GetCount(out count);
                        for (uint i = 0; i < count; i++)
                        {
                            uint fetched;
                            expansionEnumerator.Next(1, pExpansionInfo, out fetched);
                            if (fetched > 0)
                            {
                                // Convert the returned blob of data into a
                                // structure that can be read in managed code.
                                expansionInfo = (VsExpansion)Marshal.PtrToStructure(pExpansionInfo[0], typeof(VsExpansion));

                                if (!String.IsNullOrEmpty(expansionInfo.shortcut))
                                    yield return expansionInfo;
                            }
                        }

                        Marshal.FreeCoTaskMem(pExpansionInfo[0]);
                    }
                }
            }
        }
    }
}